/******************************************************************************
 *                  Shanghai ChipON Micro-Electronic Co.,Ltd
 ******************************************************************************
 *  @File Name        : kf8a100fxx_lin.c
 *  @Author           : ChipON AE/FAE Group
 *  @Date             : 2023-12-19
 *  @HW Version       : KF8A100Fxx
 *  @Lib Version      : V2.0.1
 *  @Description      : This file provides the driver for the lin module
 ******************************************************************************
 *  Copyright (C) by Shanghai ChipON Micro-Electronic Co.,Ltd
 *  All rights reserved.
 *
 *  This software is copyright protected and proprietary to
 *  Shanghai ChipON Micro-Electronic Co.,Ltd.
 *****************************************************************************/
/******************************************************************************
 *  |Date        |Version  |Author       |Description
 ******************************************************************************
 *  |2023-12-23  |V2.0     |Wang Junxiong|New creat
 *****************************************************************************/

/******************************************************************************
 **                     QAC Warnings
 *****************************************************************************/
/* PRQA S 0436 EOF #8-bit MCU Features: Direct Write Registers. */
/* PRQA S 1291, 1820, 1821, 1822, 1823, 1851, 1853, 3715, 3757, 3760 EOF #Registers are essentially unsigned numbers. */
/* PRQA S 2754 EOF #Level 7 problem due to unsupported assembly instructions. */
/* PRQA S 3768, 3771, 4130, 4393, 4434, 4443, 4446, 4461, 4532 EOF #Registers are essentially unsigned numbers. */

/******************************************************************************
 *                      Include Files
 *****************************************************************************/
#include "kf8a100fxx_lin.h"
#include "kf8a100fxx_uart.h"
/******************************************************************************
 *                      Macro Definitions
 *****************************************************************************/

/******************************************************************************
 *                      Constants
 *****************************************************************************/

/******************************************************************************
 *                      Type definitions
 *****************************************************************************/

/******************************************************************************
 *                      Variables
 *****************************************************************************/

/* PRQA S 3408 3 # Debug mode does not support the static keyword, so internal variables, functions are not modified
 * with static. */
uint8_t          Lin1_Rev_Buf_Cnt = 0u, Lin2_Rev_Buf_Cnt = 0u;
Lin_RxStatusType Lin1_Status_Flag = LIN_RX_IDLE, Lin2_Status_Flag = LIN_RX_IDLE;
/******************************************************************************
 *                      Function definitions
 *****************************************************************************/

/**
 * @brief: Get checksum value
 * LIN_CHECKSUM_CLASSIC: Lin_GetCheckSumValue(Buff[0], &Buff[1], Len - 1u);
 * LIN_CHECKSUM_ENHANCEMENT: Lin_GetCheckSumValue(PId, Buff, Len);
 *
 * @attention This function cannot be called within an interrupt service routine.
 *
 * @param PctID PID or Buff[0]
 * @param DataBuf Buff or &Buff[1]
 * @param Length Len or Len - 1
 * @retval uint8_t checksum value
 */
uint8_t Lin_GetCheckSumValue(uint8_t PctID, const uint8_t *DataBuf, uint8_t Length)
{
    uint16_t check_sum = PctID;
    uint8_t  i         = 0u;
    for (; i < Length; i++)
    {
        check_sum += DataBuf[i];
        if (check_sum > 0xFFu)
        {
            check_sum -= 255u;
        }
        else
        {
            /* nothing */
        }
    }
    return (255u - (uint8_t)check_sum);
}

/**
 * @brief: Calculate PID
 *
 * @param LinId Lin ID 0x00~0x3f
 * @retval uint8_t PID
 */
uint8_t Lin_GetParityValue(uint8_t LinId)
{
    uint8_t Lin_Bit0, Lin_Bit1, Lin_Bit2, Lin_Bit3, Lin_Bit4, Lin_Bit5, Lin_Bit6, Lin_Bit7;
    uint8_t Lin_Pid;
    Lin_Pid  = LinId & 0x3fu;
    Lin_Bit0 = Lin_Pid;
    Lin_Bit1 = Lin_Pid >> 1u;
    Lin_Bit2 = Lin_Pid >> 2u;
    Lin_Bit3 = Lin_Pid >> 3u;
    Lin_Bit4 = Lin_Pid >> 4u;
    Lin_Bit5 = Lin_Pid >> 5u;

    Lin_Bit6 = Lin_Bit0 ^ Lin_Bit1;
    Lin_Bit6 ^= Lin_Bit2;
    Lin_Bit6 ^= Lin_Bit4;
    Lin_Bit6 &= 0x01u;
    Lin_Bit6 <<= 6u;

    Lin_Bit7 = Lin_Bit1 ^ Lin_Bit3;
    Lin_Bit7 ^= Lin_Bit4;
    Lin_Bit7 ^= Lin_Bit5;
    Lin_Bit7 &= 0x01u;
    if (Lin_Bit7 > 0u)
    {
        Lin_Bit7 = 0u;
    }
    else
    {
        Lin_Bit7 = 0x80u;
    }

    Lin_Pid |= Lin_Bit6;
    Lin_Pid |= Lin_Bit7;
    return Lin_Pid;
}
/**
 * @brief UART1 initialize
 *
 * @param Prescaler = the baud rate is 16000000/[16*(prescaler+1)]
 */
void Lin1_Init(Lin_BaudRateModeType Prescaler)
{
    uint8_t Source = SCF1, RegTemp = SCF0;
    /* Enable serial module, enable receiver */
    RSCTL1 = 0x90u;
    /* Receiver idle */
    BRCTL1  = 0x50u;
    BR1CKS1 = Source;
    Source ^= RegTemp;
    BR1CKS0 = Source;
    /* Enable transmit function, shift register empty */
    TSCTL1 = 0x22u;
    /* The baud rate is 16000000/[16*(prescaler+1)] */
    EUBRGL1 = (uint8_t)Prescaler;
    EUBRGH1 = (uint8_t)((uint16_t)Prescaler >> 8u);
}

/**
 * @brief UART1 initialize
 *
 * @param Prescaler = the baud rate is 16000000/[16*(prescaler+1)]
 */
void Lin2_Init(Lin_BaudRateModeType Prescaler)
{
    uint8_t Source = SCF1, RegTemp = SCF0;
    /* Enable serial module, enable receiver */
    RSCTL2 = 0x90u;
    /* Receiver idle */
    BRCTL2  = 0x50u;
    BR2CKS1 = Source;
    Source ^= RegTemp;
    BR2CKS0 = Source;
    /* Enable transmit function, shift register empty */
    TSCTL2 = 0x22u;
    /* The baud rate is 16000000/[16*(prescaler+1)] */
    EUBRGL2 = (uint8_t)Prescaler;
    EUBRGH2 = (uint8_t)((uint16_t)Prescaler >> 8u);
}

/**
 * @brief The parity bit of temp is calculated as follows: ID6 is the odd check of ID0, ID1, ID2, and ID4, and ID7 is
 * the even check of ID1, ID3, ID4, and ID5. The result of the calculation is taken as the pid. The uart peripheral
 * is then driven to send a head signal for the Lin communication. Includes: 13-bit spacer characters, synchronous
 * characters, and pid.
 *
 * @param LinId The Lin ID number, only 0-5 digits is valid. 0x00u~0x3fu
 * @return Lin_StatusType
 * 0: LIN_OK,
 * 1: LIN_TIMEOUT,
 */
Lin_StatusType Lin1_SendHead(uint8_t LinId)
{
    uint16_t       timeout  = LIN_TIMEOUT_CONFIG;
    Lin_StatusType state    = LIN_OK;
    uint8_t        pid_temp = 0u;
    pid_temp                = Lin_GetParityValue(LinId);
    /* Enable spacer sending */
    SENDB1 = 1u;
    /* Initiate transmission */
    TXSDR1 = 0u;
    /* Waiting for transmission to complete */
    while ((TX1IF == 0u) && (timeout > 0u))
    {
        timeout = timeout - 1u;
    }
    if (timeout == 0u)
    {
        state = LIN_TIMEOUT;
    }
    else
    {
        /* Synchronized character writing */
        TXSDR1 = 0x55u;
        /* Waiting for transmission to complete */
        timeout = LIN_TIMEOUT_CONFIG;
        while ((TX1IF == 0u) && (timeout > 0u))
        {
            timeout = timeout - 1u;
        }
        if (timeout == 0u)
        {
            state = LIN_TIMEOUT;
        }
        else
        {
            /* Write Protect ID */
            TXSDR1 = pid_temp;
            /* Waiting for transmission to complete */
            timeout = LIN_TIMEOUT_CONFIG;
            while ((TX1IF == 0u) && (timeout > 0u))
            {
                timeout = timeout - 1u;
            }
            if (timeout == 0u)
            {
                state = LIN_TIMEOUT;
            }
            else
            {
            }
        }
    }
    return state;
}

/**
 * @brief The parity bit of temp is calculated as follows: ID6 is the odd check of ID0, ID1, ID2, and ID4, and ID7 is
 * the even check of ID1, ID3, ID4, and ID5. The result of the calculation is taken as the PID. The lin peripheral is
 * then driven to send a head signal for the Lin communication. Includes: 13-bit spacer characters, synchronous
 * characters, and PID.
 *
 * @param LinId The Lin ID number, only 0-5 digits is valid. 0x00u~0x3fu
 * @return Lin_StatusType
 * 0: LIN_OK,
 * 1: LIN_TIMEOUT,
 */
Lin_StatusType Lin2_SendHead(uint8_t LinId)
{
    uint16_t       timeout  = LIN_TIMEOUT_CONFIG;
    Lin_StatusType state    = LIN_OK;
    uint8_t        pid_temp = 0u;
    pid_temp                = Lin_GetParityValue(LinId);
    /* Enable spacer sending */
    SENDB2 = 1u;
    /* Initiate transmission */
    TXSDR2 = 0u;
    /* Waiting for transmission to complete */
    while ((TX2IF == 0u) && (timeout > 0u))
    {
        timeout = timeout - 1u;
    }
    if (timeout == 0u)
    {
        state = LIN_TIMEOUT;
    }
    else
    {
        /* Synchronized character writing */
        TXSDR2 = 0x55u;
        /* Waiting for transmission to complete */
        timeout = LIN_TIMEOUT_CONFIG;
        while ((TX2IF == 0u) && (timeout > 0u))
        {
            timeout = timeout - 1u;
        }
        if (timeout == 0u)
        {
            state = LIN_TIMEOUT;
        }
        else
        {
            /* Write Protect ID */
            TXSDR2 = pid_temp;
            /* Waiting for transmission to complete */
            timeout = LIN_TIMEOUT_CONFIG;
            while ((TX2IF == 0u) && (timeout > 0u))
            {
                timeout = timeout - 1u;
            }
            if (timeout == 0u)
            {
                state = LIN_TIMEOUT;
            }
            else
            {
            }
        }
    }
    return state;
}

/**
 * @brief Send the temp as data first. Then calculate the reverse code of temp, and then send the reverse code as a
 * check character
 *
 * @attention This function cannot be called within an interrupt service routine.
 *
 * @param LinId The slaver ID number, only 0-5 digits is valid. 0x00u~0x3fu
 * @param CheckSumType Type of inspection.
 * @param Buff Address for sending data
 * @param Len Length of sent data, 1u~8u
 * @return Lin_StatusType
 * 0: LIN_OK,
 * 1: LIN_TIMEOUT,
 */
Lin_StatusType Lin1_SendFrame(uint8_t LinId, Lin_CheckSumType CheckSumType, const uint8_t *Buff, uint8_t Len)
{
    uint16_t       timeout;
    const uint8_t *buff_ptr = Buff;
    Lin_StatusType state    = LIN_OK;

    uint8_t temp_1, i = 0u;

    Lin_CheckSumType check_sum_temp;

    if ((LinId == 0x3Cu) || (LinId == 0x3Du))
    {
        check_sum_temp = LIN_CHECKSUM_CLASSIC;
    }
    else
    {
        check_sum_temp = CheckSumType;
    }

    if (check_sum_temp == LIN_CHECKSUM_ENHANCEMENT)
    {
        temp_1 = Lin_GetParityValue(LinId);
        temp_1 = Lin_GetCheckSumValue(temp_1, buff_ptr, Len);
    }
    else
    {
        temp_1 = Lin_GetCheckSumValue(buff_ptr[0], &buff_ptr[1], Len - 1u);
    }

    for (; i < Len; i++)
    {
        TXSDR1 = *buff_ptr;
        buff_ptr++;
        timeout = LIN_TIMEOUT_CONFIG;
        while ((TX1IF == 0u) && (timeout > 0u))
        {
            timeout = timeout - 1u;
        }
        if (timeout == 0u)
        {
            state = LIN_TIMEOUT;
            break;
        }
        else
        {
            /* nothing */
        }
    }

    TXSDR1  = temp_1;
    timeout = LIN_TIMEOUT_CONFIG;
    while ((TX1IF == 0u) && (timeout > 0u))
    {
        timeout = timeout - 1u;
    }
    if (timeout == 0u)
    {
        state = LIN_TIMEOUT;
    }
    else
    {
        /* nothing */
    }
    return state;
}

/**
 * @brief Send the temp as data first. Then calculate the reverse code of temp, and then send the reverse code as a
 * check character
 *
 * @attention This function cannot be called within an interrupt service routine.
 *
 * @param LinId The slaver ID number, only 0-5 digits is valid. 0x00u~0x3fu
 * @param CheckSumType Type of inspection.
 * @param Buff Address for sending data
 * @param Len Length of sent data, 1u~8u
 * @return Lin_StatusType
 * 0: LIN_OK,
 * 1: LIN_TIMEOUT,
 */
Lin_StatusType Lin2_SendFrame(uint8_t LinId, Lin_CheckSumType CheckSumType, const uint8_t *Buff, uint8_t Len)
{
    uint16_t       timeout;
    const uint8_t *buff_ptr = Buff;
    Lin_StatusType state    = LIN_OK;

    uint8_t temp_2, i = 0u;

    Lin_CheckSumType check_sum_temp;

    if ((LinId == 0x3Cu) || (LinId == 0x3Du))
    {
        check_sum_temp = LIN_CHECKSUM_CLASSIC;
    }
    else
    {
        check_sum_temp = CheckSumType;
    }

    if (check_sum_temp == LIN_CHECKSUM_ENHANCEMENT)
    {
        temp_2 = Lin_GetParityValue(LinId);
        temp_2 = Lin_GetCheckSumValue(temp_2, buff_ptr, Len);
    }
    else
    {
        temp_2 = Lin_GetCheckSumValue(buff_ptr[0], &buff_ptr[1], Len - 1u);
    }

    for (; i < Len; i++)
    {
        TXSDR2 = *buff_ptr;
        buff_ptr++;
        timeout = LIN_TIMEOUT_CONFIG;
        while ((TX2IF == 0u) && (timeout > 0u))
        {
            timeout = timeout - 1u;
        }
        if (timeout == 0u)
        {
            state = LIN_TIMEOUT;
            break;
        }
        else
        {
            /* nothing */
        }
    }

    TXSDR2  = temp_2;
    timeout = LIN_TIMEOUT_CONFIG;
    while ((TX2IF == 0u) && (timeout > 0u))
    {
        timeout = timeout - 1u;
    }
    if (timeout == 0u)
    {
        state = LIN_TIMEOUT;
    }
    else
    {
        /* nothing */
    }
    return state;
}

/**
 * @brief Receive a byte, and the 9th bit parity correct return data, data in the buffer, error return 0.
 *
 * @return uint8_t data, error returns 0
 */
uint8_t Lin1_Rec_Byte(void)
{
    uint8_t temp;

    temp = RXSDR1;
    return temp;
}

/**
 * @brief Receive a byte, and the 9th bit parity correct return data, data in the buffer, error return 0.
 *
 * @return uint8_t data, error returns 0
 */
uint8_t Lin2_Rec_Byte(void)
{
    uint8_t temp;

    temp = RXSDR2;
    return temp;
}

/**
 * @brief Received data: contains 00 55 ID DATA[n] checksums
 *
 * @attention This function cannot be called within an interrupt service routine.
 *
 * @param Buff Address for data
 * @param Len Length of data, 2u~9u
 * 0: LIN_OK,
 * 1: LIN_TIMEOUT,
 */
Lin_StatusType Lin1_RecvData(uint8_t *Buff, uint8_t Len)
{
    uint16_t       timeout;
    Lin_StatusType state     = LIN_OK;
    uint8_t       *buff_temp = Buff;
    uint8_t        len_temp  = Len; /* Clear overflow errors */
    if (OVFER1 == 1u)
    {
        CRXEN1 = 0u;
        /* PRQA S 3109,3139 2 #assembly syntax. */
        _NOP();
        _NOP();
        CRXEN1 = 1u;
    }
    else
    {
        /* nothing */
    }
    for (; len_temp > 0u; len_temp--)
    {
        timeout = LIN_TIMEOUT_CONFIG;
        while ((RC1IF == 0u) && (timeout > 0u))
        {
            timeout = timeout - 1u;
        }
        if (timeout == 0u)
        {
            state = LIN_TIMEOUT;
            break;
        }
        else
        {
            /* nothing */
        }
        *buff_temp = RXSDR1;
        buff_temp++;
    }
    return state;
}

/**
 * @brief Received data: contains 00 55 ID DATA[n] checksums
 *
 * @attention This function cannot be called within an interrupt service routine.
 *
 * @param Buff Address for data
 * @param Len Length of data, 2u~9u
 * 0: LIN_OK,
 * 1: LIN_TIMEOUT,
 */
Lin_StatusType Lin2_RecvData(uint8_t *Buff, uint8_t Len)
{
    uint16_t       timeout;
    Lin_StatusType state     = LIN_OK;
    uint8_t       *buff_temp = Buff;
    uint8_t        len_temp  = Len; /* Clear overflow errors */
    if (OVFER2 == 1u)
    {
        CRXEN2 = 0u;
        /* PRQA S 3109,3139 2 #assembly syntax. */
        _NOP();
        _NOP();
        CRXEN2 = 1u;
    }
    else
    {
        /* nothing */
    }
    for (; len_temp > 0u; len_temp--)
    {
        timeout = LIN_TIMEOUT_CONFIG;
        while ((RC2IF == 0u) && (timeout > 0u))
        {
            timeout = timeout - 1u;
        }
        if (timeout == 0u)
        {
            state = LIN_TIMEOUT;
            break;
        }
        else
        {
            /* nothing */
        }
        *buff_temp = RXSDR2;
        buff_temp++;
    }
    return state;
}

/**
 * @brief Uart1 deinitialization
 *
 */
void Lin1_DeInit(void)
{
    Uart1_DeInit();
}

/**
 * @brief Uart2 deinitialization
 *
 */
void Lin2_DeInit(void)
{
    Uart2_DeInit();
}
/* EOF */
